package com.waikuai.shop.service.impl;

import cn.hutool.core.util.StrUtil;
import com.waikuai.shop.dto.LoginRequest;
import com.waikuai.shop.dto.LoginResponse;
import com.waikuai.shop.dto.RoleInfo;
import com.waikuai.shop.dto.UserInfo;
import com.waikuai.shop.entity.Role;
import com.waikuai.shop.entity.User;
import com.waikuai.shop.exception.BusinessException;
import com.waikuai.shop.mapper.PermissionMapper;
import com.waikuai.shop.mapper.RoleMapper;
import com.waikuai.shop.mapper.UserMapper;
import com.waikuai.shop.service.AuthService;
import com.waikuai.shop.service.UserService;
import com.waikuai.shop.service.WelfareCardService;
import com.waikuai.shop.util.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    private final UserMapper userMapper;
    private final UserService userService;
    private final RoleMapper roleMapper;
    private final PermissionMapper permissionMapper;
    private final JwtUtil jwtUtil;
    private final StringRedisTemplate redisTemplate;
    private final WelfareCardService welfareCardService;
    private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    private static final String CAPTCHA_PREFIX = "captcha:";
    private static final String BLACKLIST_PREFIX = "blacklist:";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginResponse login(LoginRequest loginRequest) {
        log.info("开始处理登录请求: account={}, loginType={}", loginRequest.getAccount(), loginRequest.getLoginType());

        validateLoginRequest(loginRequest);

        if ("admin".equals(loginRequest.getLoginType())) {
            if (StrUtil.isBlank(loginRequest.getCaptcha())) {
                throw new BusinessException("管理员登录需要验证码");
            }
            if (loginRequest.getCaptcha().length() != 4) {
                throw new BusinessException("验证码格式错误");
            }
        }

        User user = findByAccount(loginRequest.getAccount());
        if (user == null) {
            log.warn("登录失败：用户不存在 - {}", loginRequest.getAccount());
            throw new BusinessException("用户不存在");
        }

        if (user.getStatus() == 0) {
            log.warn("登录失败：账户已被禁用 - {}", loginRequest.getAccount());
            throw new BusinessException("账户已被禁用");
        }

        // 使用BCrypt验证密码
        if (!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
            log.warn("登录失败：密码错误 - {}", loginRequest.getAccount());
            throw new BusinessException("密码错误");
        }

        // 验证用户角色与登录类型是否匹配
        List<Role> userRoles = getUserRoles(user.getId());
        boolean isAdmin = userRoles.stream()
                .anyMatch(role -> "ROLE_ADMIN".equals(role.getRoleCode()));
        boolean isEmployee = userRoles.stream()
                .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getRoleCode()));

        if ("admin".equals(loginRequest.getLoginType())) {
            if (!isAdmin) {
                log.warn("登录失败：该账户不是管理员 - {}", loginRequest.getAccount());
                throw new BusinessException("该账户不是管理员，请使用员工登录");
            }
        } else if ("employee".equals(loginRequest.getLoginType())) {
            if (!isEmployee) {
                log.warn("登录失败：该账户不是员工 - {}", loginRequest.getAccount());
                throw new BusinessException("该账户不是员工，请使用管理员登录");
            }
            if (isAdmin && !isEmployee) {
                log.warn("登录失败：管理员账户不能使用员工登录 - {}", loginRequest.getAccount());
                throw new BusinessException("管理员账户请使用管理员登录");
            }
        }

        String token = generateTokenForUser(user);

        LoginResponse response = new LoginResponse();
        response.setToken(token);
        response.setExpiresIn(jwtUtil.getExpiration());
        response.setUserInfo(buildUserInfo(user));
        // 设置用户类型
        response.setUserType(isAdmin ? "admin" : "employee");

        // 如果是员工登录，自动兑换已发放的福利卡
        if (isEmployee && !"admin".equals(loginRequest.getLoginType())) {
            try {
                com.waikuai.shop.vo.AutoRedeemResultVO autoRedeemResult = welfareCardService.autoRedeemIssuedCards(user.getId());
                response.setAutoRedeemResult(autoRedeemResult);
                log.info("自动兑换福利卡完成，用户ID: {}, 成功: {}, 总金额: {}", 
                        user.getId(), autoRedeemResult.getSuccessCount(), autoRedeemResult.getTotalAmount());
            } catch (Exception e) {
                log.error("自动兑换福利卡失败，用户ID: {}, 错误: {}", user.getId(), e.getMessage(), e);
                // 自动兑换失败不影响登录流程
            }
        }

        userService.updateLastLoginInfo(user.getId(), getClientIp());

        log.info("用户登录成功: username={}, userId={}, userType={}", user.getUsername(), user.getId(), response.getUserType());
        return response;
    }

    @Override
    public void logout(String token) {
        if (StrUtil.isNotBlank(token)) {
            String tokenKey = BLACKLIST_PREFIX + token;
            redisTemplate.opsForValue().set(tokenKey, "1", jwtUtil.getExpiration(), TimeUnit.MILLISECONDS);
            log.info("用户登出成功");
        }
    }

    @Override
    public String refreshToken(String token) {
        if (StrUtil.isBlank(token) || !jwtUtil.validateTokenFormat(token)) {
            throw new BusinessException("无效的令牌");
        }

        String tokenKey = BLACKLIST_PREFIX + token;
        if (Boolean.TRUE.equals(redisTemplate.hasKey(tokenKey))) {
            throw new BusinessException("令牌已失效");
        }

        String newToken = jwtUtil.refreshToken(token);
        if (StrUtil.isBlank(newToken)) {
            throw new BusinessException("令牌刷新失败");
        }

        redisTemplate.opsForValue().set(tokenKey, "1", jwtUtil.getExpiration(), TimeUnit.MILLISECONDS);

        log.info("令牌刷新成功");
        return newToken;
    }

    @Override
    public User findByAccount(String account) {
        return userMapper.findByAccount(account);
    }

    @Override
    public UserInfo getUserInfoByUsername(String username) {
        User user = findByAccount(username);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }
        return buildUserInfo(user);
    }

    @Override
    public boolean validateCaptcha(String captcha, String captchaKey) {
        if (StrUtil.isBlank(captcha) || StrUtil.isBlank(captchaKey)) {
            return false;
        }

        String key = CAPTCHA_PREFIX + captchaKey;
        String cachedCaptcha = redisTemplate.opsForValue().get(key);

        if (StrUtil.isBlank(cachedCaptcha)) {
            return false;
        }

        boolean isValid = captcha.equalsIgnoreCase(cachedCaptcha);

        redisTemplate.delete(key);

        return isValid;
    }

    private void validateLoginRequest(LoginRequest loginRequest) {
        if (StrUtil.isBlank(loginRequest.getAccount())) {
            throw new BusinessException("账户不能为空");
        }
        if (StrUtil.isBlank(loginRequest.getPassword())) {
            throw new BusinessException("密码不能为空");
        }
        if (StrUtil.isBlank(loginRequest.getLoginType())) {
            throw new BusinessException("登录类型不能为空");
        }
        if (!"employee".equals(loginRequest.getLoginType()) && !"admin".equals(loginRequest.getLoginType())) {
            throw new BusinessException("无效的登录类型");
        }
    }

    private String generateTokenForUser(User user) {
        List<Role> roles = getUserRoles(user.getId());
        String roleCodes = roles.stream()
                .map(Role::getRoleCode)
                .collect(Collectors.joining(","));

        log.debug("为用户生成Token: userId={}, username={}, roles={}", user.getId(), user.getUsername(), roleCodes);
        return jwtUtil.generateToken(user.getId(), user.getUsername(), roleCodes);
    }

    private List<Role> getUserRoles(Long userId) {
        return roleMapper.findByUserId(userId);
    }

    private UserInfo buildUserInfo(User user) {
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(user, userInfo);

        // 手动转换 accountBalance: Double -> BigDecimal
        if (user.getAccountBalance() != null) {
            userInfo.setAccountBalance(BigDecimal.valueOf(user.getAccountBalance()));
        } else {
            userInfo.setAccountBalance(BigDecimal.ZERO);
        }

        List<Role> roles = getUserRoles(user.getId());
        userInfo.setRoles(roles.stream()
                .map(role -> {
                    RoleInfo roleInfo = new RoleInfo();
                    BeanUtils.copyProperties(role, roleInfo);
                    return roleInfo;
                })
                .collect(Collectors.toList()));

        List<String> permissions = getUserPermissions(user.getId());
        userInfo.setPermissions(permissions);

        return userInfo;
    }

    private List<String> getUserPermissions(Long userId) {
        List<com.waikuai.shop.entity.Permission> permissions = permissionMapper.findByUserId(userId);
        return permissions.stream()
                .map(com.waikuai.shop.entity.Permission::getPermissionCode)
                .collect(Collectors.toList());
    }

    private String getClientIp() {
        return "127.0.0.1";
    }
}
